package com.example.demo.signature;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.NonNull;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class PublicUtils {
    private static Random random = new Random();

    private PublicUtils() {
    }

    public static int getRandom7() {
        return random.nextInt(9000000) + 1000000;
    }

    public static int getRandom8() {
        return random.nextInt(90000000) + 10000000;
    }

    public static int getRandom6() {
        return random.nextInt(900000) + 100000;
    }

    public static int getRandom4() {
        return random.nextInt(9000) + 1000;
    }

    public static String encodeBase64(String s) {
        return new String(Base64.getEncoder().encode(s.getBytes()));
    }

    public static String encodeBase64(byte[] src) {
        return new String(Base64.getEncoder().encode(src));
    }

    public static String ipRegex = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\."
            + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."
            + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."
            + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$";

    public static String decodeBase64(String s) {
        return new String(Base64.getDecoder().decode(s.getBytes()));
    }

    // 判断是否是空字符串
    public static boolean isEmptyString(final String str) {
        return null == str || str.isEmpty() || "".equals(str);

    }

    /**
     * 根据字符截取一定长度的中英文混合字符串，可附加省略符
     *
     * @param str
     * @param len   要截取的长度(中文和英文长度都为1)
     * @param elide 省略符
     * @return String 截取后的字串
     */
    public static String subCharString(String str, int len, String elide) {
        if (isEmptyString(str)) {
            return "";
        }
        try {
            int strLen = str.length();
            if (len >= strLen || len < 1) {
                return str;
            }
            byte[] strByte = str.getBytes("utf-8");
            int currentLen = 0;
            int count = 0;
            int i = 0;
            while (currentLen < len && i < strByte.length) {
                int v = (int) strByte[i];
                if (v < 0) {
                    if (++count == 3) {
                        count = 0;
                        currentLen++;
                    }
                } else {
                    currentLen++;
                }
                i++;
            }
            return new String(strByte, 0, i, "utf-8") + elide.trim();
        } catch (UnsupportedEncodingException e) {
            log.error("subCharString error,data:" + str);
            return "";
        }
    }

    /**
     * 判断字符串列表是否为空列表或者空文字列表
     *
     * @param list
     * @return
     */
    public static boolean isEmptyStringList(List<String> list) {
        if (list == null || list.size() == 0) {
            return true;
        }
        for (String str : list) {
            if (!isEmptyString(str.trim())) {
                return false;
            }
        }
        return true;
    }

    public static boolean isEmptyList(Collection<?> collection) {
        return collection == null || collection.size() == 0;
    }

    // 字节数组转成16进制字符串
    public static String byteToHexString(byte[] input) {
        String output = "";

        for (int i = 0; i < input.length; i++) {
            String tmp = Integer.toHexString(input[i] & 0xFF);
            if (tmp.length() == 1) {
                output += "0" + tmp;
            } else {
                output += tmp;
            }
        }

        return output;
    }

    // 获取key,采用随机数实现
    public static String getRandomKey(int len) {
        StringBuffer keyBuf = new StringBuffer("");

        // 生成多段的字符串,拼接到一起
        for (int i = 0; i < len; i++) {
            keyBuf.append((int) (Math.random() * 10));
        }

        return keyBuf.toString();
    }

    // 将字符串生成MD5摘要
    public static String getMD5String(String Key) {
        MessageDigest md5;

        try {
            md5 = MessageDigest.getInstance("MD5");
            md5.update(Key.getBytes());
            byte out[] = md5.digest();

            return byteToHexString(out);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            log.warn(e.getLocalizedMessage());
        }
        return "";
    }

    // 将字符串生成sha1摘要
    public static String getSHA1String(String Key) {
        MessageDigest md;

        try {
            md = MessageDigest.getInstance("SHA-1");
            byte[] out = md.digest(Key.getBytes());

            return byteToHexString(out);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            log.warn(e.getLocalizedMessage());
        }
        return "";
    }

    // 将字符串生成sha1摘要
    public static String getSHA2String(String Key) {
        MessageDigest md;

        try {
            md = MessageDigest.getInstance("SHA-256");
            byte[] out = md.digest(Key.getBytes());

            return byteToHexString(out);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            log.warn(e.getLocalizedMessage());
        }
        return "";
    }

    /**
     * 打印异常栈
     *
     * @param e
     * @return
     */
    public static String getStackTrace(Exception e) {
        StringBuffer sb = new StringBuffer();

        StackTraceElement[] steArray = e.getStackTrace();
        for (StackTraceElement ste : steArray) {
            sb.append(ste.toString()).append("\n");
        }

        return sb.toString();
    }

    // 获取当前的时间戳,用于数据库操作, add by wsy
    public static Timestamp getCurrentTimestamp() {
        return new Timestamp(System.currentTimeMillis());
    }

    // 获取指定偏移天数的时间戳
    public static Timestamp getTimestamp(final int offsetDay) {
        long currTs = System.currentTimeMillis();
        currTs += offsetDay * 24 * 3600 * 1000;

        return new Timestamp(currTs);
    }

    // 时间转换为字符串的函数
    public static String getLocalDate(final Date time) {
        return DateFormat.getDateInstance().format(time);
    }

    public static String getLocalDate(final Timestamp time) {
        return DateFormat.getDateInstance().format(time);
    }

    public static String getLocalTime(final Date time) {
        return DateFormat.getDateTimeInstance().format(time);
    }

    public static String getLocalTime(final Timestamp time) {
        return DateFormat.getDateTimeInstance().format(time);
    }

    public static boolean isBitTrue(int value, int bit_index) {
        int i;
        i = value >> bit_index & 1;
        return (i == 1) ? true : false;
    }

    public static int setValueByBit(int value, int bit_index, boolean b) {
        int i;
        if (b) {
            i = 1 << bit_index | value;
        } else {
            i = ~(1 << bit_index) & value;
        }
        return i;
    }

    public static boolean isTelephone(String telephone) {
        return telephonePattern.matcher(telephone).find();
    }

    public static boolean isEmail(String email) {
        return emailPattern.matcher(email).find();
    }

    public static boolean isEffectivePwd(String password) {
        return passwordPattern.matcher(password).find();
    }

    public static String escapeHtml(String htmlStr) {
        htmlStr = htmlStr.replaceAll("\\<br/\\>", "/r/n");

        Matcher m_script = scriptPattern.matcher(htmlStr);
        htmlStr = m_script.replaceAll(""); //过滤script标签

        Matcher m_html = htmlPattern.matcher(htmlStr);
        htmlStr = m_html.replaceAll(""); //过滤html标签

        htmlStr = htmlStr.replaceAll("/r/n", "<br/>");
        return htmlStr;
    }

    public static boolean hasKeyword(String s, String keyword) {
        if (isEmptyString(keyword)) {
            return false;
        }
        if (!currentKeyword.equals(keyword)) {
            currentKeyword = keyword;
            keywordPattern = Pattern.compile(keyword.replaceAll(",", "|"));
        }
        if (null == keywordPattern) {
            return false;
        }
        return keywordPattern.matcher(s).find();
    }

    public static String getTableNo(int id) {
        return getTableNo(id, 16);
    }

    public static String getTableNo(long id) {
        return getTableNo(id, 16);
    }

    public static String getTableNo(long id, int baseNum) {
        return String.valueOf(id % baseNum);
    }

    public static String getTableNo(int id, int baseNum) {
        return String.valueOf(id % baseNum);
    }

    public static String clearIllegalChar(String input) {
        return StringUtils.replaceChars(input, illegalChars, "");
    }

    public static final String illegalChars = "+-&|!(){}[]^”~*?:\\'";

    public static final Pattern telephonePattern = Pattern.compile("^\\d{11}$");
    public static final Pattern emailPattern = Pattern.compile("^([a-zA-Z0-9_\\.\\-])+\\@(([a-zA-Z0-9\\-])+\\.)+([a-zA-Z0-9]{2,4})+$");
    public static final Pattern htmlPattern = Pattern.compile("<[^>]+>"); //定义HTML标签的正则表达式
    public static final Pattern scriptPattern = Pattern.compile("<script[^>]*?>[\\s\\S]*?<\\/script>", Pattern.CASE_INSENSITIVE);
    //public static final Pattern passwordPattern = Pattern.compile("^(?![\\d]+$)(?![a-zA-Z]+$)(?![^\\da-zA-Z]+$).{6,}$"); // 密码至少6位，至少包含数字、大写字母、小写字母中的两种
    public static final Pattern passwordPattern = Pattern.compile("^(?!^\\d+$)(?!^[a-zA-Z]+$)(?!^[~!@#$%^&*()\\-_+={}[\\\\]\\\\;:,.<>/?]+$)[~!@#$%^&*()\\-_+={}[\\\\]\\\\;:,.<>/?\\w]{6,20}$"); // 密码至少6位，至少包含数字、字母、特殊符号中的两种
    /**
     * 数字或字母的正则表达式
     */
    public static final Pattern alphaNumericPattern = Pattern.compile("^[A-Za-z0-9]+$");
    public static Pattern keywordPattern = null;
    public static String currentKeyword = "";

    public static String getSignature(String appKey, String appSecret, String nonce) throws Exception {
        return new String(Base64.getEncoder().encode(HMACSHA1.HmacSHA1Encrypt(getAuthContent(appKey, nonce), appSecret)));
    }

    public static String getAuthContent(String appKey, String nonce) {
        return "appKey=" + appKey + "&nonce=" + nonce;
    }

    /**
     * 是否https并跨域<br>
     * 1.只支持本地file跨域;在pr插件调用云端接口时使用<br>
     *
     * @param req http请求
     * @return true:https并跨域
     */
    public static boolean isNeedHttpsCross(HttpServletRequest req) {
        String origin = req.getHeader("origin");
        if (StringUtils.isEmpty(origin)) {
            origin = req.getHeader("Origin");
        }
        if (StringUtils.isEmpty(origin)) {
            return false;
        }
        return origin.startsWith("file");
    }

    /**
     * 如  http://localhost:18080/cloud/aaa/a  获取的是 http://localhost:18080/cloud  cloud为项目根路径
     *
     * @param req
     * @return
     */
    public static String getHttpServerUrl(HttpServletRequest req) {
        StringBuffer url = req.getRequestURL();
        String server = url.delete(url.length() - getHttpServerUrlPath(req).length(), url.length()).toString();
        return server;
    }

    /**
     * 如：http://localhost:18080/cloud/aaa/a 获取的是 http://localhost:18080
     *
     * @param
     * @return
     */
    public static String getHttpDomain(HttpServletRequest request) {
        StringBuffer requestURL = request.getRequestURL();
        String requestURI = request.getRequestURI();
        return requestURL.delete(requestURL.length() - requestURI.length(), requestURL.length()).toString();

    }

    /**
     * 如  http://localhost:18080/cloud/aaa/a  获取的是 /aaa/a   ,cloud为项目根路径
     *
     * @param req
     * @return
     */
    public static String getHttpServerUrlPath(HttpServletRequest req) {
        String path = req.getPathInfo(); //todo 优化
        if (path == null) {
            path = req.getServletPath();
        }
        //todo 目前getPathInfo和 getServletPath 在 console-server 和 h5-server 中取到的值不同，需要尽快处理
        return path;
    }

    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-Real-IP");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // 获取第一个IP
        if (ip != null && ip.indexOf(",") > -1) {
            ip = ip.substring(0, ip.indexOf(","));
        }
        return ip;
    }

    public static String getIpAddr() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return null;
        }
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest request = servletRequestAttributes.getRequest();
        if (request == null) {
            return null;
        }
        return getIpAddr(request);
    }

    public static String getSign(Map<String, Object> map, String key) {
        ArrayList<String> list = new ArrayList();
        Iterator i$ = map.entrySet().iterator();

        while (i$.hasNext()) {
            Map.Entry<String, Object> entry = (Map.Entry) i$.next();
            if (entry.getValue() != "") {
                list.add((String) entry.getKey() + "=" + entry.getValue() + "&");
            }
        }

        int size = list.size();
        String[] arrayToSort = (String[]) list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < size; ++i) {
            sb.append(arrayToSort[i]);
        }

        String result = sb.toString();
        result = result + "key=" + key;
        result = getMD5String(result).toUpperCase();
        return result;
    }

    public static String getFileType(String path) {
        if (!isEmptyString(path)) {
            int idx = path.lastIndexOf(".");
            if (idx > -1) {
                return path.substring(idx + 1, path.length()).toLowerCase();
            }
        }
        return "";
    }


//    public static void main(String args[]) throws Exception {
//        String policy = "{\"statement\": [{\"action\": [\"name/cos:*\"],\"effect\": \"allow\",\"resource\":\"*\"}],\"version\": \"2.0\"}";
//        String region = "sh";
//        String secretId = "AKIDlJdgT0PFyRdbJmm0lHYZLs9ByWptoFXa";
//        String secretKey = "vbCXcS2kDRQ9dstjMkuoTs3zg7JGyRec";
//        int duration = 1800;
//        String name = "admin";
//        String method = "GET";
//        String path = "sts.api.qcloud.com/v2/index.php";
//        String  scheme = "https://";
//        String timestamp = System.currentTimeMillis()/1000 + "";
//        String nonce = System.currentTimeMillis() + "";
//        String action = "GetFederationToken";
//
//        Map<String, String> params = new HashMap<String, String>();
//        params.put("Action", action);
//        params.put("Region", region);
//        params.put("Timestamp", timestamp);
//        params.put("Nonce", nonce);
//        params.put("SecretId", secretId);
//        params.put("name", name);
//        params.put("policy", policy);
//        params.put("durationSeconds", duration + "");
//
//
//        String sign = encrypt(method, path, params, secretKey);
//        params.put("Signature", sign);
//
//        System.out.println(sign);
//
//        String query  = formatMapToStr(params, true);
//        query =  scheme + path + "?" + query;
//        System.out.println(query);
//    }

    public static String encrypt(String method, String path, Map<String, String> key_values, String secretKey) throws Exception {
        TreeMap<String, String> sortedParams = new TreeMap<>();
        sortedParams.putAll(key_values);
        String formatParameters = formatMapToStr(sortedParams, false);
        formatParameters = method + path + "?" + formatParameters;

        String signature = URLEncoder.encode(new String(Base64.getEncoder().encode(HMACSHA1.HmacSHA1Encrypt(formatParameters, secretKey))));
        return signature;
    }

    private static String formatMapToStr(Map<String, String> kVMap, boolean encode) {
        StringBuilder strBuilder = new StringBuilder();
        boolean seeOne = false;
        for (String key : kVMap.keySet()) {
            String lowerKey = key;
            String encodedValue = "";
            if (kVMap.get(key) != null) {
                encodedValue = kVMap.get(key);
            }
            if (!seeOne) {
                seeOne = true;
            } else {
                strBuilder.append("&");
            }
            strBuilder.append(lowerKey).append("=").append(encodedValue);
        }
        return strBuilder.toString();
    }

    private static String buildTimeStr(Date expiredTime) {
        StringBuilder strBuilder = new StringBuilder();
        long startTime = System.currentTimeMillis() / 1000;
        long endTime = expiredTime.getTime() / 1000;
        strBuilder.append(startTime).append(";").append(endTime);
        return strBuilder.toString();
    }

    /**
     * 填充url请求参数
     *
     * @param baseUrl 基础url路径
     * @param params  参数列表
     * @return 填充后的url
     */
    public static String urlParamsFill(String baseUrl, Map<String, String> params) {
        if (isEmptyString(baseUrl) || params == null || params.entrySet().size() < 1) {
            return baseUrl;
        }
        StringBuilder sb = new StringBuilder(baseUrl);
        sb.append("?");
        for (Map.Entry<String, String> e : params.entrySet()) {
            sb.append(e.getKey());
            sb.append("=");
            sb.append(e.getValue());
            sb.append("&");
        }
        return sb.substring(0, sb.length() - 1);
    }

    public static String urlEncode(String url) {
        try {
            return URLEncoder.encode(url, "utf-8");
        } catch (Exception e) {
            return url;
        }
    }

    public static InputStream getInputStreamFromUrl(String urlStr) {
        if (isEmptyString(urlStr)) {
            return null;
        }
        try {
            // 创建URL
            URL url = new URL(urlStr);
            // 创建链接
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(10 * 1000);
            if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
                return null;//连接失败/链接失效/图片不存在
            }
            return conn.getInputStream();
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 获取指定m3u8的总时长信息
     *
     * @param m3u8Url 访问地址
     * @return 总时长, 单位毫秒
     */
    public static long getM3u8DurationByUrl(String m3u8Url) {
        if (StringUtils.isEmpty(m3u8Url) || !m3u8Url.startsWith("http")) {
            return 0;
        }
        long duration = 0L;
        InputStreamReader inputStreamReader = null;
        final String EXT_INF_TAG = "#EXTINF:";
        try {
            URL url = new URL(m3u8Url);
            inputStreamReader = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str;
            while (((str = bufferedReader.readLine()) != null)) {
                //按行读取m3u8头文件,#EXTINF:后的内容就是每一个分片的时长,单位为秒
                if (str.contains(EXT_INF_TAG)) {
                    str = str.substring(EXT_INF_TAG.length(), str.lastIndexOf(","));
                    double doubleDuration = Double.parseDouble(str) * 1000;
                    duration += (long) doubleDuration;
                }
            }
        } catch (Exception e) {
            log.error("m3u8解析失败,m3u8Url:" + m3u8Url, e);
        } finally {
            if (inputStreamReader != null) {
                try {
                    inputStreamReader.close();
                } catch (IOException ignored) {
                }
            }
        }
        return duration;
    }

    public static String sanitizeHtml(String htmlContent) {
        // 过滤文章内容中的html
        htmlContent = htmlContent.replaceAll("</?[^<]+>", "");
        // 去除字符串中的空格 回车 换行符 制表符 等
        htmlContent = htmlContent.replaceAll("\\s*|\t|\r|\n", "");
        // 去除空格
        htmlContent = htmlContent.replaceAll("&nbsp;", "");
        // 去掉其他一些字符
        htmlContent = htmlContent.replaceAll("\\\\", "");
        htmlContent = htmlContent.replaceAll("\\\\\\\\", "");

        return htmlContent;
    }

    public static String filterStr(String text) {
        String regEx = "[`~$^*!?()_\\-|{}':'\\[\\].<>/~！？￥……*（）——|{}【】‘：”“’、]";
        Pattern p = Pattern.compile(regEx);
        Matcher m = p.matcher(text);
        text = m.replaceAll("").trim();
        return text.replaceAll(" ", "");
    }

    public static String getUrlDomainName(String fullUrl) {
        try {
            URL url = new URL(fullUrl);
            return url.getHost();
        } catch (MalformedURLException e) {
            log.error("URL解析错误:" + fullUrl);
            return "";
        }
    }

    public static String getSuffixName(String path) {
        if (StringUtils.isEmpty(path)) {
            return "";
        } else {
            String[] split = path.split("[.]");
            if (split.length == 2) {
                return "." + split[1];
            }
        }
        return "";
    }

    public static String text2html(String text) {
        if (isEmptyString(text)) {
            return text;
        }
        return text.replaceAll("\r\n", "<br>").
                replaceAll("\n", "<br>").
                replaceAll("\r", "<br>").
                replaceAll(" ", "&nbsp;");
    }

    public static boolean ipCheck(String text) {
        if (text != null && !text.isEmpty()) {
            // 判断ip地址是否与正则表达式匹配
            if (text.matches(ipRegex)) {
                return true;
            } else {
                return false;
            }
        }
        return false;
    }

    /**
     * 校验单流包装、云导播满足1080P以下及4通道
     *
     * @return
     */
    public static boolean checkChannelNumAnd1080P(Integer channelNum, Integer width, Integer height) {
        if (channelNum == null) {
            //校验单流包装
            return width != null && height != null
                    && ((width <= 1920 && height <= 1080) || (width <= 1080 && height <= 1920));
        } else {
            //云导播
            return channelNum != null && width != null && height != null
                    && channelNum == 4
                    && ((width <= 1920 && height <= 1080) || (width <= 1080 && height <= 1920));
        }
    }

    public static boolean checkByPattern(String word, @NonNull Pattern pattern) {
        if (word == null || word.isEmpty()) {
            return false;
        }
        return pattern.matcher(word).find();
    }

    public static void main(String[] args) {

        System.out.println(urlEncode("Nn0TpLBG0222gwQvcRe3t7O6Xek="));

        System.out.println(urlEncode("89ptkqgIEhvokccVxJfu+SK4t4M="));

        System.out.println(urlEncode("http://localhost/#/activity/list"));
//        System.out.println(text2html("    123模压基材基斯柯达堪模压基材基斯柯达堪模压基材基斯柯达堪模压基材基斯柯达堪模压基材基斯柯达堪模压基材基斯柯达堪。\n" +
//                "    模压基材基斯柯达堪。\n"));
    }

}
